Antecedentes

Walmart es la cadena de retail más grande en el mundo; fue fundada por Sam Walton 1962 y desde entonces ha tenido un gran crecimiento llegando a cotizar en la bolsa de Nueva York en 1972. Actualmente Walmart ha entendido el valor de la información y con sus 20,000 tiendas en 28 países entiende que los datos que generan están muy poco aprovechados. Con esto en mente Walmart ha creado una nube privada que enfrenta alrededor de 2.5 Petabytes de información cada hora y ha creado también “The Data Café” que es un hub punta de lanza en analítica con la finalidad de aprovechar de mejor manera esa cantidad de información. La necesidad de encontrar insights que puedan accionar cambios es lo que está moviendo a la empresa y sus esfuerzos en el mediano y largo plazo.

Objetivo

En el presente proyecto, se realizó un modelo para categorizar las visitas que realizan los clientes a Walmart basándose únicamente en los productos que compran los clientes en su visita. La base se obtuvo de kaggle de la competencia titulada “Walmart Recruiting: Trip Type Classification”.

Las categorías están representadas por números enteros no contiguos, y no se conoce el significado de ninguna categoría.

La función que se busca optimizar, más específicamente minimizar, es la pérdida logarítmica multiclase también conocida como log loss multiclase, misma que se define como:

\(-\frac{1}{N}\sum_{i=1}^{N}\sum_{j=1}^{M}y_{ij}log(p_{ij})\)

donde \(N\) es el total de visitas en el conjunto de prueba, \(M\) es el total de categorías existentes (como más adelante se verá \(M = 38\)), \(y_{ij}\) es 1 si la observación \(i\) pertenece a la categoría \(j\) y 0 en otro caso, \(p_{ij}\) es la probabilidad obtenida por el modelo que la observación \(i\) pertenezca a la categoría \(j\).

Criterio de Éxito

Obtener un valor de la función log loss menor a 1 sobre el conjunto de prueba.

Lectura y limpieza de datos

Se realiza la lectura y limpieza de datos con las funciones utils, load, prepare y clean:

source("utils.R")
source("00-load.R")
## [1] "walmart.rds se bajó y guardó\n"
source("01-prepare.R")
source("02-clean.R")

La base de datos original muestra un registro por cada producto que se compró en las diferentes visitas de los clientes en la tienda. Por ejemplo, si un cliente en su visita compró 10 productos diferentes, esa visita tiene 10 registros con el mismo identificador de visita y mismo tipo de visita. Los primeros renglones de la base son los siguientes:

##   TripType VisitNumber Weekday         Upc ScanCount DepartmentDescription
## 1      999           5  Friday 68113152929        -1    FINANCIAL SERVICES
## 2       30           7  Friday 60538815980         1                 SHOES
## 3       30           7  Friday  7410811099         1         PERSONAL CARE
## 4       26           8  Friday  2238403510         2 PAINT AND ACCESSORIES
## 5       26           8  Friday  2006613744         2 PAINT AND ACCESSORIES
## 6       26           8  Friday  2006618783         2 PAINT AND ACCESSORIES
##   FinelineNumber
## 1           1000
## 2           8931
## 3           4504
## 4           3565
## 5           1017
## 6           1017

Las variables con las que se cuenta en la base de datos son:

  • TripType - es una variable categórica que indica el tipo de visita que realizó una persona en Walmart, es la variable objetivo que se busca predecir
  • VisitNumber - es el identificador único para cada visita que realizaron los diferentes clientes a Walmart
  • Weekday - día de la semana en que el cliente realizó la visita a Walmart
  • Upc - es un código para identificar de forma única los productos de Walmart
  • ScanCount - número de productos de un tipo específico que se compararon, si el valor es -1 indica una devolución de producto
  • DepartmentDescription - departamente al cual pertenece el producto
  • FinelineNumber - código para identificar los productos de una forma más general que el código Upc pero más granular que el departamento

La estructura original de la base no tiene registros únicos a nivel visita, esto complica el ajuste del modelo pues las categorías son a nivel visita. Si se trabajara con la base original, se estaría ajustando una categoría a nivel producto en lugar de a nivel visita. Por lo tanto, el tratamiento que se realizó a los datos consistió en construir la base de forma que tenga un solo renglón por visita. La variable de día de la semana es a nivel visita, por lo que no sufrió modificaciones. Respecto a las variables del departamento y número de productos, se crearon tantas columnas como departamentos existen, 68 en total, y se sumaron de acuerdo al número de productos por departamento comprados por visita. La base creada y utilizada para modelar luce como sigue:

## # A tibble: 10 x 71
## # Groups:   TripType, VisitNumber, Weekday [10]
##    TripType VisitNumber Weekday HR_PHOTO ACCESSORIES AUTOMOTIVE BAKERY
##       <int>       <int> <chr>      <dbl>       <dbl>      <dbl>  <dbl>
##  1        3         106 Friday         0           0          0      0
##  2        3         121 Friday         0           0          0      0
##  3        3         153 Friday         0           0          0      0
##  4        3         162 Friday         0           0          0      0
##  5        3         164 Friday         0           0          0      0
##  6        3         177 Friday         0           0          0      0
##  7        3         181 Friday         0           0          0      0
##  8        3         188 Friday         0           0          0      0
##  9        3         203 Friday         0           0          0      0
## 10        3         265 Friday         0           0          0      0
## # ... with 64 more variables: BATH_AND_SHOWER <dbl>, BEAUTY <dbl>,
## #   BEDDING <dbl>, BOOKS_AND_MAGAZINES <dbl>, BOYS_WEAR <dbl>,
## #   BRAS__SHAPEWEAR <dbl>, CAMERAS_AND_SUPPLIES <dbl>,
## #   CANDY_TOBACCO_COOKIES <dbl>, CELEBRATION <dbl>, COMM_BREAD <dbl>,
## #   CONCEPT_STORES <dbl>, COOK_AND_DINE <dbl>, DAIRY <dbl>,
## #   DSD_GROCERY <dbl>, ELECTRONICS <dbl>, FABRICS_AND_CRAFTS <dbl>,
## #   FINANCIAL_SERVICES <dbl>, FROZEN_FOODS <dbl>, FURNITURE <dbl>,
## #   GIRLS_WEAR_46X__AND_74 <dbl>, GROCERY_DRY_GOODS <dbl>, HARDWARE <dbl>,
## #   HEALTH_AND_BEAUTY_AIDS <dbl>, HOME_DECOR <dbl>, HOME_MANAGEMENT <dbl>,
## #   HORTICULTURE_AND_ACCESS <dbl>, HOUSEHOLD_CHEMICALS_SUPP <dbl>,
## #   HOUSEHOLD_PAPER_GOODS <dbl>, IMPULSE_MERCHANDISE <dbl>,
## #   INFANT_APPAREL <dbl>, INFANT_CONSUMABLE_HARDLINES <dbl>,
## #   JEWELRY_AND_SUNGLASSES <dbl>, LADIES_SOCKS <dbl>, LADIESWEAR <dbl>,
## #   LARGE_HOUSEHOLD_GOODS <dbl>, LAWN_AND_GARDEN <dbl>,
## #   LIQUORWINEBEER <dbl>, MEAT__FRESH__FROZEN <dbl>,
## #   MEDIA_AND_GAMING <dbl>, MENSWEAR <dbl>, `NULL` <dbl>,
## #   OFFICE_SUPPLIES <dbl>, OPTICAL__FRAMES <dbl>, OPTICAL__LENSES <dbl>,
## #   OTHER_DEPARTMENTS <dbl>, PAINT_AND_ACCESSORIES <dbl>,
## #   PERSONAL_CARE <dbl>, PETS_AND_SUPPLIES <dbl>, PHARMACY_OTC <dbl>,
## #   PHARMACY_RX <dbl>, PLAYERS_AND_ELECTRONICS <dbl>,
## #   PLUS_AND_MATERNITY <dbl>, PRE_PACKED_DELI <dbl>, PRODUCE <dbl>,
## #   SEAFOOD <dbl>, SEASONAL <dbl>, SERVICE_DELI <dbl>,
## #   SHEER_HOSIERY <dbl>, SHOES <dbl>, SLEEPWEAR_FOUNDATIONS <dbl>,
## #   SPORTING_GOODS <dbl>, SWIMWEAR_OUTERWEAR <dbl>, TOYS <dbl>,
## #   WIRELESS <dbl>

En cuanto a la limpieza, ésta consistió en unificar dos departamentos MENS WEAR con MENSWEAR; para todas las variables de departamento se cambiaron los NA’s por 0, pues en realidad los valores nulos de esas variables son porque se realizaron cero compras de ese departamento en la respectiva visita. En los nombres de las variables, se sustituyeron los espacios por guiones bajos y se eliminaron caracteres especiales (las comas y los &) para facilitar el manejo de la base en el análisis exploratorio.

De esta manera, se obtiene una base de datos con 95’674 registros (únicos a nivel visita) y 69 variables explicativas.

Análisis Exploratorio

En primer lugar, se analiza la variable objetivo: el tipo de visita realizada en Walmart. Su distribución luce como se muestra en la siguiente gráfica:

Se observa que existen categorías que sobresalen respecto a las demás, estas categorías son: 8, 9, 39, 40 y 999. Por el contrario, existen categorías que tienen muy poca presencia en los datos, por ejemplo la 12, 14 y 23, estas categorías con poca representación en los datos muy probablemente serán complicadas de modelar. Para tener mayor detalle se obtienen las frecuencias por categoría:

##     3     4     5     6     7     8     9    12    14    15    18    19 
##  3643   346  4593  1277  5752 12161  9464   269     4   978   549   375 
##    20    21    22    23    24    25    26    27    28    29    30    31 
##   637   641   928   139  2609  3698   503   785   492   433  1081   594 
##    32    33    34    35    36    37    38    39    40    41    42    43 
##  1984  1315   719  2030  3005  2788  2912  9896  6130   583  1858   872 
##    44   999 
##  1187  8444

Análisis Univariado

Se realiza ahora el análisis univariado sobre las variables explicativas.

El día de la semana tiene la siguiente distribución:

Notoriamente, la mayoría de las visitas se realizan entre el viernes y el domingo, principalmente el fin de semana. Resulta curioso que el día de la semana con menos visitas es el jueves.

Las visitas realizadas cada día de la semana son las siguientes:

##    Monday   Tuesday Wednesday  Thursday    Friday  Saturday    Sunday 
##     12027     11530     11612     11243     15234     16904     17124

El departmento al cual pertenecen los productos comprados y devueltos tiene la siguiente distribución:

Como era de esperarse, existen departamentos con mucha población y departamentos con escasa población.

Es importante recordar que la estructura de la base transformada tiene una columna por cada departamento, por lo tanto la distribución real de cada variable de departamento tiene distribuciones muy concentradas en el cero, por ejemplo:

Incluso en el departamento más pobaldo GROCERY DRY GOODS, la distribución está bastante concentrada en el cero:

Análisis Bivariado

Resulta complicado visualizar las gráficas de la variable objetivo con las variables explicativas, pues el tipo de visita puede tomar 38 valores diferentes. Por ello, se realizarán gráficas separadas, mostrando de 10 en 10 las categorías.

La distribución del día de la semana en que se realiza la visita, para cada categoría se muestra a continuación:

Teniendo en mente la gráfica univariada del día de la semana que se mostró en la sección anterior, y contrastando con la distribución de esta variable para cada categoría, se pueden detectar patrones de cada uno de los tipos de visita. Mencionaremos algunos de ellos:

En la categoría 3, la distribución del día de la semana es bastante diferente a la global, pues tienen un nivel bajo el lunes, comienza a subir, disimuye el jueves y llega a su máximo en viernes, descendiendo el fin de semana.

En la categoría 25, las visitas a Walmart tienen su menor número en lunes, y van a aumentando día a día llegando a su máximo el domingo.

Para la categoría 40, el patrón es igual al global, pero las diferencias del nivel en los diferentes días es más acentuada que en la distribución global.

Es importante mencionar, que todos los tipos de vísita alcanzan su máximo entre el viernes y el domingo.

Respecto a la variable objetivo y los distintos departamentos, nuevamente se separa en 4 gráficas y se acotó el rango del eje x de -1 a 10 para facilitar su entendimiento e interpretación.

La gráfica bivariada de la variable objetivo y el departamento con mayores compras GROCERY DRY GOODS se muestra a continuación:

Se observa que en la mayoría de las categorías, el departamento GROCERY DRY GOODS está concenctrado en el cero, pero existen categorías con distribuciones que muestran concentraciones en valores positivos. Por ejemplo 12, 15, 44 y más notoriamente en las categorías 37, 38, 39 y 40. Se puede pensar que estas categorías pueden referirse a visitas a Walmart orientadas a compras de este tipo de productos.

Ahora se analiza la distribución del departamento DSD GROCERY para cada tipo de categoría:

Este departamento resulta ser más interesante, pues tiene muchas menos categorías concentradas en el cero. Por ejemplo, la categoría 35 parece tener muy poca población en el cero a diferencia del departamento que se mostró anteriormente.

Ahora se realiza el análisis sobre un departamento menos poblado que los anteriores, éste es PHARMACY OTC:

En este departamento, llaman mucho la atención las categorías 4 y 5 pues son las que tienen una distribución más alejada del cero. Esto lleva a pensar que estas categorías están ligadas con la compra de productos farmacéuticos.

El análisis se vuelve más complicado cuando los departamentos están poco poblados, por ejemplo en JEWELRY AND SUNGLASSES, las gráficas son las siguientes:

En este departamento se redujo más el rango del eje x, de -1 a 5 para tener mejor visualización. La distribución de joyería y lentes de sol tiene una altísima concentración en el cero para prácticamente todas las categorías.

Análisis Multivariado

Dada la cantidad de variables con las que se cuenta y los 38 valores que toma del tipo de visita, el análisis multivariado tiene muchas posibles combinaciones. A continuación se muestran algunas de ellas.

Se puede obtener por ejemplo, la distribución conjunta de 2 departamentos con la variable objetivo (para facilitar la visualización, se separan por grupos de categorías):

Resulta difícil interpretar las gráficas para las categorías poco pobladas. Cuando las categorías están más pobladas, por ejemplo en la 37, se observa que estos dos departamentso PRODUCE y DSD GROCERY, están dispersos el primero en valores menores a 20 y el segundo en valores menores a 10. Por el contrario en la categoría 40, los valores de PRODUCE son menores a 15 y los valores de DSD GROCERY son menores a 25.

Feature Engineering

En cuanto a la ingeniería de características, la construcción de la base a nivel visita fue el paso esencial, pues permite encontrar un modelo más acertivo, y mas que limpieza de datos fue feature engineering . La construcción de las variables de productos comprados y/o devueltos por departamento también formaron parte del feature engineering.

Adicionalmente, se construyeron las siguientes variables a partir de las disponibles originalmente:

  • Total de productos comprados en la visita

  • Indicadora de devolución de productos, vale 1 si se devolvió al menos un producto en la visita y 0 en otro caso

  • Total de productos devueltos en la visita

  • Total de departamentos visitados, si se compró al menos un producto en el departamento éste cuenta como 1 en la suma de departamentos visitados

  • Productos diferentes comprados respecto a UPC, es decir cuántos productos con diferente código UPC se compraron en la visita

  • Día del mes en que se realizó la visita, se explica más adelante esta variable

El total de productos comprados tiene la siguiente distribución:

En la mitad de las visitas realizadas, los clientes compran 4 productos, éste es el valor de su mediana:

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -52.000   2.000   4.000   7.499   9.000 311.000

La indicadora devolución de productos luce como sigue:

Aproximadamente, en el 11.6% de las visitas se realiza alguna devolución de producto. Este porcentaje puede considerarse alto.

La distribución de productos devueltos está concentrada en el cero, pues como se vio en la indicadora, en la mayoría de las visitas no se devuelven productos.

De la distribución de la variable Departamentos visitados se concluye que en la mayoría de las visitas los clientes compran productos de máximo 5 departamentos diferentes:

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   1.000   2.000   3.337   5.000  26.000

En cuanto al número de productos con diferente UPC, la mayoría de los clientes compran en su visita productos de máximo 8 códigos UPC distintos.

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   2.000   3.000   6.602   8.000 207.000

Sobre la variable del día del mes, se realizó el siguiente análisis para llegar a su construcción: Al juntar las bases train y test, ordenarlas por el código de visita y graficar la distribución de la variable día de la semana se observó la siguiente gráfica:

En esta gráfica, el valor 1 en el eje y representa el viernes, el valor 2 el sábado, así sucesivamente hasta el valor 7 que representa al jueves. Al observarla, lleva a pensar que de alguna forma, los códigos de visita están ligados con el tiempo, parece ser que se trata de 31 días iniciando con un jueves y finalizando en domingo.

Por lo tanto se construyó la variable día del mes, numerando de 1 a 31 los diferentes días de la semana de acuerdo a su código de visita. Su distribución es la siguiente:

Preparar base para procesar en Python

Finalmente en R, se guardan los datos como un archivo feather para poder realizar el ajuste de modelos en Python.

path <- "Datos/walmart.feather"
write_feather(walmartSpread, path)

Modelos

Todos los modelos se calcularon en Python, siguiendo esta metodología:

  • Se parte la base train en train (train del train) y test (test del train), utilizando muestreo estratificado por la variable objetivo
  • Se realiza un grid de hiperparámetros de acuerdo al tipo de modelo (regresión, bosques, etc)
  • Se calculan los modelos con el train del train, sobre el grid de hiperparámetros realizando cross validation
  • Siguiendo el criterio de optimizar la función log loss, se elige el mejor conjunto de hiperparámetros
  • Se calcula el desempeño del modelo con los mejores hiperparámetros sobre el test del train
  • Se ajusta un modelo con los mejores hiperparámetros sobre toda la base train
  • Se realiza el predict sobre el conjunto de prueba
  • Se sube esta predicción a kaggle para obtener el desempeño final del modelo

Se ajustaron los siguientes modelos: regresión logística multinomial, bosques aleatorios, máquinas de soporte vectorial, red neuronal. El desempeño final (evaluación de kaggle) de cada uno fue:

Modelo Parámetros Tiempo Ajuste Public Score
Regresión Multinomial penalización L1, C = 1 3 hrs 1.3185
Bosque Aleatorio depth = 100, feat = sqrt, split = 10, n_est = 1500 4 hrs 2.1320
Máquina de Soporte Vectorial kernel lineal, C = 10 5 hrs 1.0820
Red Neuronal alpha 100, 30 capas, 1000 iter, semilla 0 4 hrs 1.0370

El mejor ajuste se obtuvo con la red neuronal consiguiendo un log loss muy cercano a 1, es importante mencionar que la máquina de soporte tuvo un desempeño muy similar. Nuestro mejor modelo nos situaría en el lugar 445 del total de 1047 participantes en la competencia, esto es el percentil 42%.

Por último, se ajustó un modelo siguiendo la página http://kaslemr.github.io/Walmart_Kaggle_Competition/, donde el competidor menciona el método final que utilizó para ajustar su modelo.

El competidor utilizó la liberaría XGB la cual contiene un algoritmo de gradient boosting. Este algoritmo consiste en ensambles de árboles de decisión. La diferencia de esta liberería respecto a otras de ensambles, es el parámetro para forzar a que termine el modelo después de no mejorar en cierto número de iteraciones. De esta manera evita el sobreajuste y su tiempo de entretanmiento suele ser bajo.

El modelo se ajustó siguiendo su código, parte la base de entrenamiento en train y en test para la construcción del modelo, sin usar validación cruzada ni optimización de hiperparámetros. Tardó 20 minutos en terminar de ajustarse. El desempeño final (kaggle) que se obtuvo fue de 0.9647, logrando así la métrica de éxito (log loss menor a 1), lo cual nos situaría en la posición 411, percentil 39%.